<?php
/***
 * Candy框架 数据库操作的基类	
 * 
 * $Author: 刘森 (fingerboy@qq.com) $
 * $Date: 2019-08-05 10:48:32 $   
 */
 
declare(strict_types=1);
namespace Candy\Extend\DB;
use Candy\Core\{Debug,Validate,Tpl,Container};

abstract Class DBServer {
	protected $msg = ''; //提示消息数组
	protected $tabName = '';  //表名，自动获取
	protected $fieldList = [];  //表字段结构，自动获取
	protected $auto;
	protected static $echosql = '';
	protected $charset;
	protected $host;
	protected $user;
	protected $pass;
	protected $dbname;
	protected $tabprefix;
	protected $setNull = true;
	public $path;
	//SQL的初使化
	public $sql = ['field'=>'','where'=>'', 'order'=>'', 'limit'=>'', 'group'=>'', 'having'=>'', 'join'=>'', 'fetch'=>'', 'token'=>''];
	
	/**
     * 设置文件驱动名称
     * @param string $name 驱动名称
     * @return static
     */
    public static function instance($name = null): DBServer
    {
        $class = ucfirst(strtolower(is_null($name) ? G('DRIVER') : $name));
        if (class_exists($object = "\\Candy\\Extend\\DB\\Driver\\{$class}")) {
			$instance = Container::getInstance()->get($object);
			if(is_null($instance)){
				$instance = Container::getInstance()->bind($object, new $object)->get($object);
			}
			return $instance;
        } else {
            throw new \Exception("DB driver [{$class}] does not exist.");
        }
    }
	
	/*
	 * 通过指定的配置文件设置对应的数据库连接
	 */
	protected function setLink(): void
	{
		if(is_null(C('HOST')) || is_null(C('USER')) || is_null(C('PASS')) ||  is_null(C('DBNAME'))){
			Debug::showMessage('请先配置数据库。', 'Mysql');
		}else{
			$this->charset = C('CHARSET');
			$this->host = C('HOST');
			$this->user = C('USER');
			$this->pass = C('PASS');
			$this->dbname = C('DBNAME');
			$this->tabprefix = C('TABPREFIX');
		}
		
	}
	
	/** 
	 * 用来获取表名
	 */
	public function __get(string $pro)
	{
		if($pro == 'tabName')
			return $this->tabName;
	}

	/**
	 * 用于重置成员属性
	 */
	protected function setNull(): void
	{
		if($this->setNull)
			$this->sql = ['field'=>'','where'=>'', 'order'=>'', 'limit'=>'', 'group'=>'', 'having'=>'', 'join'=>'', 'fetch'=>'', 'token'=>''];
	}

	/**
	 *连贯操作调用field() where() order() limit() group() having()方法，组合SQL语句
	 */
	public function __call(string $methodName,array $args): object
	{
		$methodName = strtolower($methodName);
		if(array_key_exists($methodName, $this->sql)){
			if(empty($args[0]) || (is_string($args[0]) && trim($args[0]) === '')){
				$this->sql[$methodName] = '';
			}else{
				$this->sql[$methodName] = $args;
			}

			if($methodName == 'limit'){
				if($args[0] == '0')
					$this->sql[$methodName] = $args;
			}	
		}else{
			Debug::addmsg('<font color=\'red\'>调用类'. $this::class ."中的方法{$methodName}()不存在!</font>", 4);
		}
		return $this;
	}

	/**
	 * 按指定的条件获取结果集中的记录数
	 */
	public function total(...$args): int
	{
		//左关联
		$join = self::comJoin();
		
		//where条件
		$where = ['where'=>'','data'=>[]];
		$data = [];
		if(count($args)>0){
			$where = $this->comWhere($args, $join['as']);
		}else if($this->sql['where'] != ''){
			$where = $this->comWhere($this->sql['where'], $join['as']);
		}
		$data = array_merge($where['data'], $join['joinDate']);
		$where = $where['where'] . $join['joinWhere'];
		//sql
		$sql = 'SELECT COUNT(*) as count FROM `'. $this->tabName .'`' . (empty($join['as']) ? '' : ' AS ' . $join['as'][0]) . $join['join'] . $where;
		return $this->query($sql, __METHOD__,$data);			
	}
	
	/**
	 * 获取查询多条结果，返回二维数组
	 */
	public function select(...$args)
	{
		//左关联
		$join = self::comJoin();
		
		//字段
		$fields = self::getFormatStr('fields', ' ', $this->sql['field'], $this->fieldList, $join['as']);
		//joinField
		if(count($join['joinField']) > 0)
			$fields .= ',' .implode(',', $join['joinField']);
		
		//where条件
		$where = ['where'=>'','data'=>[]];
		$data = [];
		if(count($args)>0){
			$where = $this->comWhere($args, $join['as']);
		}else if($this->sql['where'] != ''){
			$where = $this->comWhere($this->sql['where'], $join['as']);
		}
		$data = array_merge($where['data'], $join['joinDate']);
		$where = $where['where'] . $join['joinWhere'];
		
		//order
		$order = self::getFormatStr('order', ' ORDER BY ', $this->sql['order'], [$this->fieldList['pri']], $join['as']) . $join['joinOrder'];
		//group
		$group = self::getFormatStr('group', ' GROUP BY ', $this->sql['group'], [], $join['as']) . $join['joinGroup'];
		if(!empty($group) && stripos($group, 'GROUP BY') === false)
			$group = ' GROUP BY ' . trim($group, ',') . ' ' ;
		
		//having
		$having = self::getFormatStr('having', ' HAVING ', $this->sql['having'], [], $join['as']);
		if(empty($having))
			$having = $join['joinHave'];
		
		//limit
		$limit = $this->sql['limit'] != '' ? $this->comLimit($this->sql['limit']) : '';
		$sql = 'SELECT '.$fields.' FROM `'. $this->tabName .'`' . (empty($join['as']) ? '' : ' AS ' . $join['as'][0]) . $join['join'] . $where. $group . $having . $order . $limit;
		return $this->query($sql, __METHOD__,$data);	
	}
	
	/**
	 * 获取一条记录，返回一维数组
	 */
	public function find(string $pri = '')
	{
		//左关联
		$join = self::comJoin();
		
		//字段
		$fields = self::getFormatStr('fields', ' ', $this->sql['field'], $this->fieldList, $join['as']);
		//joinField
		if(count($join['joinField']) > 0)
			$fields .= ',' .implode(',', $join['joinField']);
		
		
		//where条件
		$where = $this->sql['where'];
		is_string($where) && $where = [$where];
		if($pri != ''){
			$where[] = $pri;
		}
		
		$where = $this->comWhere($where, $join['as']);
		$data = array_merge($where['data'], $join['joinDate']);
		$where = $where['where'] . $join['joinWhere'];
		
		//order
		$order = self::getFormatStr('order', ' ORDER BY ', $this->sql['order'], [$this->fieldList['pri']], $join['as']) . $join['joinOrder'];
		$sql = 'SELECT '.$fields.' FROM `'. $this->tabName .'`' . (empty($join['as']) ? '' : ' AS ' . $join['as'][0]) . $join['join'] . $where. $order .' LIMIT 1';
		return $this->query($sql,__METHOD__,$data);
	}
	
	//filter = 1 去除 " ' 和 HTML 实体， 0则不变
	private function check(array $array,int $filter): array
	{
		$arr = [];
	
		foreach($array as $key=>$value){
			if(empty($value)) continue;
			$key = strtolower($key);
			if(in_array($key, $this->fieldList)/* && $value !== ''*/){
				if(is_array($filter) && !empty($filter)){
					if(in_array($key, $filter) || !is_string($value)){
						$arr[$key] = $value;	
					}else{
						$arr[$key] = stripslashes(htmlspecialchars($value));
					}
				}else if(!$filter || !is_string($value)){
					$arr[$key] = $value;
				}else{
					$arr[$key] = stripslashes(htmlspecialchars($value));
				}
			}	
		}
		return $arr;
	}
	
	/**
	 * 向数据库中插入一条记录
	 */
	public function insert(array $array = [],int $filter = 1,int $validate = 0)
	{
		if(empty($array))
			$array = $_POST;

		if($validate || (isset($array['token']) && !empty($array['token']))){
			$vali = Validate::check($array, 'add', $this);
		}else{
			$vali = true;	
		}

		if($vali){  
			$array = $this->check($array, $filter);
			$sql = "INSERT INTO {$this->tabName}(`".implode('`,`', array_keys($array)).'`) VALUES ('.implode(',', array_fill(0, count($array), '?')) . ')';
			return $this->query($sql,__METHOD__,array_values($array));
		}else{
			$this->msg = Validate::getMsg();
			return false;
		}
	}
	
	/**
	 * insert别名方法
	 */
	public function add(array $array = [],int $filter = 1,int $validate = 0)
	{
		return $this->insert($array, $filter, $validate);
	}
	
	/**
	 * 更新数据表中指定条件的记录
	 */
	public function update(array $array = [],int $filter = 1,int $validate = 0)
	{
        if(empty($array))
			$array = $_POST; 

		if($validate || (isset($array['token']) && !empty($array['token']))){
			$vali = Validate::check($array, 'mod', $this);
		}else{
			$vali = true;	
		}

		if($vali){  
			$data = [];
	      	if(is_array($array)){
				$array = array_change_key_case($array);
				if(array_key_exists($this->fieldList['pri'], $array)){
					$pri_value = $array[$this->fieldList['pri']];
					unset($array[$this->fieldList['pri']]);	
		       	}

				$array = $this->check($array, $filter); 
   				$s = '';
   				foreach ($array as $k=>$v){

				 	$s .= "`{$k}`=?,";
				 	$data[] = $v;  //value
			 	}
			 	$s = rtrim($s, ',');
    			$setfield = $s;
			}else{
				$setfield = $array;
				$pri_value = '';
			
			}
			
			$order = $this->sql['order'] != '' ?  " ORDER BY {$this->sql["order"][0]}" : '';
			$limit = $this->sql['limit'] != '' ? $this->comLimit($this->sql["limit"]) : '';
			
			if($this->sql['where'] != ''){
				$where = $this->comWhere($this->sql['where']);
				$sql = "UPDATE  {$this->tabName} SET {$setfield}".$where['where'];
				
				if(!empty($where['data'])){
					foreach($where['data'] as $v){
						$data[] = $v; //value
					}
				}
				$sql .= $order.$limit;
			}else{
				$sql = "UPDATE {$this->tabName} SET {$setfield}  WHERE {$this->fieldList["pri"]}=?";
				$data[] = $pri_value; //value
			}
			
			//优化0更新update
			$result = $this->query($sql, __METHOD__, $data);	
			if($result === 0){
				//改变记录的CSRFTOKEN
				session('CSRFTOKEN', '123');
				self::setMsg('数据没有变动');
			}
			return $result;
		}else{
			$this->msg = Validate::getMsg();
			return false;
		}
	}
	
	/**
	 * update别名方法
	 */
	public function save(array $array = [],int $filter = 1,int $validate = 0)
	{
		return $this->update($array, $filter, $validate);
	}
	
	/**
	 * 删除满足条件的记录		 
	 */
	public function delete(...$args): int|bool
	{
		$where = '';
		$data = [];
		
		//验证token
		$vali = true;
		if(!empty($this->sql['token']))
			$vali = Validate::checkToken($this->sql['token'][0]);
		
		if($vali){
			if(count($args)>0){
				$where = $this->comWhere($args);
				$data = $where['data'];
				$where = $where['where'];
			}else if($this->sql['where'] != ''){
				$where = $this->comWhere($this->sql['where']);
				$data = $where['data'];
				$where = $where['where'];
				
			}
			
			$order = $this->sql['order'] != '' ?  " ORDER BY {$this->sql["order"][0]}" : '';
			$limit = $this->sql['limit'] != '' ? $this->comLimit($this->sql["limit"]) : '';
			
			if($where == '' && $limit == ''){
				$where = " where {$this->fieldList["pri"]}=''";
			}
			
			$sql = "DELETE FROM {$this->tabName}{$where}{$order}{$limit}";
			return $this->query($sql, __METHOD__, $data);
		}else{
			$this->msg = Validate::getMsg();
			return false;
		}
	}
	
	/**
	 * 数据自增
	 */
	public function setInc(string $field = null,int $num = 1)
	{
		if(is_null($field))
			return false;
		
		//验证token
		$vali = true;
		if(!empty($this->sql['token']))
			$vali = Validate::checkToken($this->sql['token'][0]);
		
		if($vali){
			$data = [];
			if($this->sql['where'] != ''){
				$where = $this->comWhere($this->sql['where']);
				$data = $where['data'];
				$sql = "UPDATE  {$this->tabName} SET `{$field}` = `{$field}` + $num".$where['where'];
			}else{
				$sql = "UPDATE  {$this->tabName} SET `{$field}` = `{$field}` + $num WHERE 1";
			}
			return $this->query($sql, __METHOD__, $data);
		}else{
			$this->msg = Validate::getMsg();
			return false;
		}
	}
	
	/**
	 * 数据自减
	 */
	public function setDec(string $field = null,int $num = 1)
	{
		if(is_null($field))
			return false;
		
		//验证token
		$vali = true;
		if(!empty($this->sql['token']))
			$vali = Validate::checkToken($this->sql['token'][0]);
		
		if($vali){
			$data = [];
			if($this->sql['where'] != ''){
				$where = $this->comWhere($this->sql['where']);
				$data = $where['data'];
				$sql = "UPDATE {$this->tabName} SET `{$field}` = `{$field}` - $num".$where['where'];
			}else{
				$sql = "UPDATE  {$this->tabName} SET `{$field}` = `{$field}` - $num WHERE 1";
			}
			return $this->query($sql, __METHOD__, $data);
		}else{
			$this->msg = Validate::getMsg();
			return false;
		}
	}
	
	/**
	 * 数据拼接
	 */
	public function truncate()
	{
		$vali = true;
		if(!empty($this->sql['token']))
			$vali = Validate::checkToken($this->sql['token'][0]);
		
		if($vali){
			$sql = "TRUNCATE {$this->tabName} ";
			return $this->query($sql, __METHOD__, []);
		}else{
			$this->msg = Validate::getMsg();
			return false;
		}
	}
	
	/**
	 * 数据拼接
	 */
	public function conCat(string $field = null,string $str = '')
	{
		if(is_null($field) || empty($str))
			return false;
		
		$vali = true;
		if(!empty($this->sql['token']))
			$vali = Validate::checkToken($this->sql['token'][0]);
		
		if($vali){
			$data=[];
			if($this->sql['where'] != ''){
				$where=$this->comWhere($this->sql['where']);
				$data=$where['data'];
				$sql="UPDATE {$this->tabName} SET `{$field}` = CONCAT(`{$field}`,'{$str}') ".$where['where'];
			}
			return $this->query($sql, __METHOD__, $data);
		}else{
			$this->msg = Validate::getMsg();
			return false;
		}
	}
	
	private function comLimit(array $args)
	{
		if(count($args) == 2){
			return " LIMIT {$args[0]},{$args[1]}";
		}else if(count($args) == 1){
			return " LIMIT {$args[0]}";
		}else{
			return '';
		}	
	}
	
	/**
	 * 处理join情况 
	 */ 
	private function comJoin(): array
	{
		//左关联
		$joinPri = '';
		$join = [
			'as'=>'',
			'join'=>'',
			'joinField'=>[],
			'joinWhere'=>'',
			'joinDate'=>[],
			'joinOrder'=>'',
			'joinGroup'=>'',
			'joinHave'=>'',
		];
		if(!empty($this->sql['join'])){
			//开启左关联
			$join['as'] = str_replace($this->tabprefix, '', $this->tabName)[0] . '.';
			
			$i = 1;
			foreach($this->sql['join'] as $tab){
				list($tabName, $field, $fk) = $tab;
				//as
				if(is_array($tabName)){
					[$tabName, $joinTableAs] = $tabName;
				}else{
					$joinTableAs = strtolower($tabName)[0] . $i;
					$i++;
				}
				//join on
				is_array($fk) ? [$fk, $pri] = $fk : $pri = $this->fieldList['pri'];
				$join['join'] .= ' LEFT JOIN ' . $this->tabprefix . $tabName . ' AS ' . $joinTableAs . ' ON ' . $join['as'] . '`' . $pri . '` = '. $joinTableAs .'.`' . $fk .'`';
				
				//fields
				is_string($field) && $field = [$field];
				$join['joinField'][] = self::getFormatStr('fields', ' ', $field, '*', $joinTableAs . '.');
				
				//where order group have
				if(isset($tab[3])){
					//where
					if(!empty($tab[3][0])){
						$cachefile = RUNTIME . 'Data/'.$tabName.'.php';
						if(file_exists($cachefile)){
							$joinPri = (array)json_decode(substr(ltrim(file_get_contents($cachefile),'<?php '), 0, -3), true)['pri'];	
						}
						if(isset($tab[3][0][1])){
							//OR
							$joinInfo = $this->comWhere($tab[3][0][1], $joinTableAs. '.', ' OR ' . (count($tab[3][0][1]) > 1 ? '(' : ''), $joinPri);
							$join['joinWhere'] .= $joinInfo['where'] . (count($tab[3][0][1]) > 1 ? ')' : '');
						}else{
							//AND
							$joinInfo = $this->comWhere($tab[3][0][0], $joinTableAs. '.', ' AND ', $joinPri);
							$join['joinWhere'] .= $joinInfo['where'];
						}
						$join['joinDate'] = array_merge($join['joinDate'], $joinInfo['data']);
					}
					//order
					if(!empty($tab[3][1])){
						$join['joinOrder'] = ',' . self::getFormatStr('order', '', $tab[3][1], [], $joinTableAs. '.');
					}
					//group
					if(!empty($tab[3][2])){
						$join['joinGroup'] = ',' . self::getFormatStr('group', '', $tab[3][2], [], $joinTableAs. '.');
					}
					// HAVING 
					if(!empty($tab[3][3])){
						$join['joinHave'] = self::getFormatStr('group', ' HAVING ', $tab[3][3], [], $joinTableAs. '.');
					}
				}
			}
		}
		return $join;
	}
	
	/**
	 * 用来组合SQL语句中的where条件 
	 */ 
	private function comWhere(array $args,string $join = '',string $where = ' WHERE ',string $pri = ''): array
	{
		empty($pri) && $pri = $this->fieldList['pri'];
		
		$data = [];
		if(empty($args)){
			return ['where'=>'', 'data'=>$data];
		}
		
		if(count($args) == 2 && is_string($args[0]) && is_array($args[1])){
			return ['where'=>$where.$join.$args[0], 'data'=>$args[1]];
		}
		
		//解析多维数组情况
		[$sql, $data] = self::analysisWhere($args, $join, $pri);
		//最外层()
		if(empty($sql)){
			$sql = ' 1=1 ';
		}else{
			if($sql[0] == '(')
				$sql = substr($sql, 1, strlen($sql)-2);
		}
		
		$where .= $sql;
		return ['where'=>$where, 'data'=>$data];
	}
	
	protected function analysisWhere(array $args,string $join = '',string|null $pri = ''): array
	{
		$where = '';
		$data = [];
		foreach($args as $key=>$option){
			if(empty($option)){
				$where .= ''; //条件为空，返回空字符串；如'',0,false 返回： '' //5
				continue;
			}else if(is_string($option) || (is_numeric($option) && !is_numeric($key))){
				if (is_numeric($key)){
					if (is_numeric($option)){
						//数字类型
						$where .= (empty($where) ? ' ' : ' AND ') . $join .'`'. $pri . '` = ? '; //2
					} else {
						if(stripos($option, ',') && is_numeric($option[0])){
							//1,2,4类型
							$option = explode(',', $option); //3
							$where .= (empty($where) ? ' ' : ' AND ') . $join .'`'. $pri .'` IN(' . implode(',', array_fill(0, count($option), '?')) . ')';
						}else{
							//sql语句类型
							$where .= $option; //2
							continue;
						}
					}
					//存放变量
					if(is_array($option)){
						$data = array_merge($data, $option);
					}else{
						$data[] = $option;
					}
					continue;
				}else{
					//关联数据
					if (is_numeric($option)){
						//数字类型
						if(strpos($key, ' ')){
							$where .= (empty($where) ? ' ' : ' AND ') . $join . $key.'?';
						}else{
							$where .= (empty($where) ? ' ' : ' AND ') . $join .'`'. $key . '` = ? '; //2
						}
					} else {
						if(stripos($option, ',') && is_numeric($option[0])){
							$option = explode(',', $option); //3
							$where .= (empty($where) ? ' ' : ' AND ') . $join .'`'. $key .'` IN(' . implode(',', array_fill(0, count($option), '?')) . ')';
						}elseif(isset($option[0]) && ($option[0] == '%' || substr($option[0], -1) == '%')){
							$where .= (empty($where) ? ' ' : ' AND ') . $join .'`'. $key .'` LIKE ?';
						}elseif(strpos($key, ' ')){
							$where .= (empty($where) ? ' ' : ' AND ') . $join . $key .'?';
						}else{
							$where .= (empty($where) ? ' ' : ' AND ') . $join .'`'. $key . '` = ? '; //2
						}
					}
					$data[] = $option;
					continue;
				}
			}else if(is_numeric($option)){
				$where .= $join . '`' . $pri .'` = ?';   //1
				$data[0] = $option;
				continue;
			}else if(is_array($option)){
				if(is_numeric($key)){
					[$nWhere, $ndata] = self::analysisWhere($option, $join, $pri);
					if(count($option) >1){
						$nWhere = '('. $nWhere .')';
					}
					$where = rtrim($where, 'AND ') . (empty($where) ? '' : (\Candy\Extend\Arr::isAssocArray($args) ? ' AND ' : ' OR ')) . $nWhere;
					$data = array_merge($data, $ndata);
				}
			}
			$where .= ' AND ';
		}
		
		$where = rtrim($where, 'AND ');
		return [$where, $data];
	}
	
	protected function getFormatStr(string $name,string $head, $source, $default = [],string $as = ''): string
	{
		$return = $source != '' ? 
				  (isset($source[1]) ? 
				  trim($source[1]) : 
				  (stripos($source[0], ',') === false ? 
				  [$source[0]] : 
				  explode(',', $source[0]))) :
				  $default;
		if(is_array($return)){
			if(count($return) > 0){
				if(count($return) > 1){
					//多字段
					$return = $head . $as . implode(', '. $as, array_map(function ($v){
						return stripos(trim($v), '`') === false ? (stripos(trim($v), ' ') === false ? '`' . trim($v) . '`' : trim($v)) : trim($v);
					}, $return));
				}else{
					//单字段
					$return = $head . $as . (stripos(trim($return[0]), '`') === false ?  (stripos(trim($return[0]), ' ') === false ? '`' . trim($return[0]) . '`' : trim($return[0])) : trim($return[0]));
				}
			}else{
				return '';
			}
		}else{
			if($return == '*') return $as . $return;
			
			//手写严格模式
			if(stripos($return, '`') === false){
				if(stripos($return, ',') === false){
					return $as . '`' . $return . '`';
				}else{
					Tpl::shutDownTpl('手动输入'. $name .'参数字段名称必须以 <b>`</b> 括起来！');
				}
			}
			
			if(!empty($this->sql['join']) && stripos($return, '.') === false){
				Tpl::shutDownTpl('左关联模式下手动输入'. $name .'参数字段名称必须带上表头');
			}
		}
		
		return $return;
	}
	
	protected function escapeStringArray(array $array): array
	{
		//驱动自带转义暂停处理
		return $array;
		if(empty($array))
			return [];
		
	 	$value = [];
		foreach($array as $val){
			$value[] = str_replace(['"', "'"], '', $val);
		}
	 	return $value;
	}
	
	/**
	 * 输出完整的SQL 语句，用于调试
	 */
	protected function sql(string $sql,array $params_arr): string
	{
		if (false === strpos($sql, '?') || count($params_arr) == 0) return $sql;
		
   		// 进行 ? 的替换，变量替换
    	if (false === strpos($sql, '%')){
       		// 不存在%，替换问号为s%，进行字符串格式化
       		$sql = str_replace('?', "'%s'", $sql);
			array_unshift($params_arr, $sql);
        	return call_user_func_array('sprintf', $params_arr); //调用函数和所用参数
    	}
	}
	
	/*
	 *仅用于调试程序使用
	 */
	public function psql(): void
	{
		if(empty(self::$echosql)){
			echo '<br>没有SQL语句执行！<br>';
		}else {
			echo '<br>最后执行的SQL语句：<b>' . self::$echosql . '</b><br>';
		}
	}

	/** 
	 * 关联查询，参数为数组，可以有多个，每个数组为一个关联的表
	 */
	public function r_select(...$args): array
	{
		if(count($args) == 0 || !is_array($args[0]))
			return false;
		
		$one = $this->select();
		$pri = $this->fieldList['pri'];
		$pris = [];
		
		if(count($one) > 0){
			foreach($one as $row){
				$pris[] = $row[$pri];
			}
		}
		
		foreach($args as $tab){
			list($tabName, $field, $fk) = $tab;
			if(!empty($field)){
				if(!in_array($fk, explode(',', $field))){
					$field = $field.','.$fk;	
				}else{
					$field = $field;
				}
			}else{
				$field = '';
			}	 
			//以子数组的方式1:n
			if(!empty($tab[3])){
				$sub = $tab[3];
				if(is_array($sub)){
					$obj = D($tabName);
					$new = [];
					foreach($one as $row){
						//$where = array($fk=>$row[$pri]);
						$where = "{$fk}={$row[$pri]}";
						if(!empty($sub[3])){
							$where .= " AND {$sub[3]}";
						}
						
						if(!empty($sub[1])){
							if(!empty($sub[2])){
								//处理一条的特殊情况
								if($sub[2] == 1){
									$row[$sub[0]] = $obj->field($field)->order($sub[1])->where($where)->find();
								}else{
									$row[$sub[0]] = $obj->field($field)->order($sub[1])->limit($sub[2])->where($where)->select();
								}
							}else{
								$row[$sub[0]] = $obj->field($field)->order($sub[1])->where($where)->select();
							}
						}else{
							if(!empty($sub[2])){
								if($sub[2] == 1){
									$row[$sub[0]] = $obj->field($field)->where($where)->find();
								}else{
									$row[$sub[0]] = $obj->field($field)->limit($sub[2])->where($where)->select();
								}
							}else{
								$row[$sub[0]] = $obj->field($field)->where($where)->select();
							}
						}
						$new[] = $row;					 
					}
					
					$one = $new;
				}else {
					$new = [];
					$npris = [];
					
					if(count($one) > 0){
						foreach($one as $row){
							$npris[] = $row[$sub];
						}
					}
					
					//以平级数组的方式1:1	
					$where = [$fk=>$npris];
					
					if(!empty($where[$fk])){	 
						$data = D($tabName)->field($field)->where($where)->select();
						$i = 0;
						foreach($one as $row){
							foreach($data as $read){
								if($read[$fk] == $row[$sub]){
									foreach($read as $k3=>$v3){
										if(array_key_exists($k3, $row)){
											$row[$tabName.'_'.$k3] = $v3;
										}else{
											$row[$k3] = $v3;
										}
									}
									$new[$i] = $row;
									break;
								}
							}
							
							if(empty($new[$i])){
								$new[$i] = $one[$i];
							}
							
							$i++;							
						}
						$one = $new;
					}
				}
			}else{
				$new = [];
				//以平级数组的方式1:1	
				$where = [$fk=>$pris];
				
				if(!empty($where[$fk])){	 
					$data = D($tabName)->field($field)->where($where)->select();
					foreach($data as $row){
						foreach($one as $read){
							if($read[$pri] == $row[$fk]){
								foreach($row as $k3=>$v3){
									if(array_key_exists($k3, $read)){
										$read[$tabName.'_'.$k3] = $v3;
									}else{
										$read[$k3] = $v3;
									}
								}
								$new[] = $read;	
							}
						}
					}
					$one = $new;
				}
			}
		}
		return $new;
	}
	
	/**
	 *  关联删除
	 */
	public function r_delete(...$args): int
	{
		$vali = true;
		if(!empty($this->sql['token']))
			$vali = Validate::checkToken($this->sql['token'][0]);
		
		if($vali){
			if(count($args) == 0 || !is_array($args[0]))
				return false;
			
			$one = $this->select();
			$pri = $this->fieldList['pri'];
			$pris = [];
			
			foreach($one as $row){
				$pris[] = $row[$pri];
			}
			
			$affected_rows = 0;
			foreach($args as $tab){
				$where = [$tab[1]=>$pris];
				
				if(!empty($tab[2]))
					$where = array_merge($where, $tab[2]);
				
				if(!empty($where[$tab[1]]))
					$affected_rows += D($tab[0])->where($where)->delete();
			}
			
			$affected_rows += $this->where($pris)->delete();
			
			return $affected_rows;
		}else{
			$this->msg = Validate::getMsg();
			return false;
		}
	}
	
	/**
	 *  格式化数据
	 */
	public function page(...$args): array
	{
		$simple = true;
		$config = [];
		$select = true;
		if(empty($args)){
			$page = C('page') ?: (I('get.page') ?: 1);	//页数
			$number = C('limit') ?: (I('get.limit') ?: 10);	//每页的数量
			//分页配置
			if(C('pageConfig')){
				$config = C('pageConfig');
				$simple = false;
			}
		}else{
			$page = is_numeric($args[0]) ? $args[0] : (C('page') ?: 1);	//页数
			$number = is_numeric($args[1]) ? $args[1] : (C('number') ?: 10);	//页数
			//分页配置
			if(!empty($args[3]) || C('pageConfig')){
				$config = empty($args[3]) ? C('pageConfig') : $args[3];
				$simple = false;
			}
			if(!empty($args[2])){
				$select = false;
				$r_select = $args[2];
			}
		}
		
		$listNumber = C('listNumber') ?: 5;	//页数表个数
		$start = floor($listNumber/2);
		
		//总数
		$this->setNull = false;
		$total = $this->total();
		$this->setNull = true;
		
		//数据
		$skip = ($page - 1) * $number;
		$list = $select ? $this->limit($skip, $number)->select() : $this->limit($skip, $number)->r_select($r_select);
		
		//分页模式
		if($simple){
			$totalpage = ceil($total/$number);
			$first = 1;
			$pre = $page > 1 ? $page - 1 : 1;
			$next = $page < $totalpage ? $page + 1 : $totalpage;
			$last = $totalpage;
			$data = ['number'=>$number, 'total'=>$total, 'totalpage'=>$totalpage, 'first'=>$first, 'last'=>$last, 'nowpage'=>$page];
			
			//上一页
			if($pre != $page)
				$data['pre'] = $pre;
			
			//下一页
			if($next != $page)
				$data['next'] = $next;
			
			//list
			$pages = [];
			if($totalpage > $listNumber){
				$start = ($page + $start) > $totalpage ? 
				(($page - $start) > 1 ? ($page + $start - $totalpage) > ($page - $start) ? 1 : ($totalpage - 2 * $start) : 1) : (($page - $start) > 1 ? $page - $start : 1);
				$end = $start + $listNumber;
				for($i=$start;$i<$end;$i++){
					$pages[] = $i;
				}
			}else{
				for($i=1;$i<=$totalpage;$i++){
					$pages[] = $i;
				}
			}
			$data['pages'] = $pages;
			
			//fetch
			if($this->sql['fetch'] != '' && $totalpage != 1){
				$fetchName = $this->sql['fetch'][0];
				$view = N('View');
				$view->assign('page', $data);
				$data = $view->compileFile($fetchName);
			}else{
				$data = '';
			}
		}else{
			$pageClass = N('Page');
			$pageClass->loadConfig($config);
			$pageClass->total($total);
			$pageClass->num($number);
			$pageClass->page($page);
			$pageClass->center($start);
			$pageClass->circle($listNumber);
			$data = $pageClass->output();
		}
		
		return ['list'=>$list,'page'=>$data];
	}	
	
	/**
	* 返回消息
	*
	* @return	obj	json对象
	*/
	public function jsonResult(array $data = []): string
	{
		return json($data);
	}
	
	/**
	* 返回消息
	*
	* @return	array	数组
	*/
	public function arrayResult(array $data = []): array
	{
		return $data;
	}
	
	/**
	* 设置提示信息
	*
	* @param	mixed	$mess	最后一次错误
	*/
	public function setMsg(string $mess): void
	{
		$this->msg = $mess;
	}
	
	/**
	* 获取提示信息
	*
	* @return	string	提示消息字符串
	*/
	public function getMsg(): string
	{
		return $this->msg;
	}
	
	abstract function query(string $sql,string $method,array $data = []);
	abstract function setTable(string $tabName);
	abstract function beginTransaction();
	abstract function commit();
	abstract function rollBack();
	abstract function dbSize();
	abstract function dbVersion();
}
